package template;

import java.util.List;

import no.hib.dpf.core.Arrow;
import no.hib.dpf.core.Constraint;
import no.hib.dpf.core.Predicate;

public class StringUtil {
	private enum Constraints {
		XOR_SPLIT("XOR_SPLIT"), XOR_MERGE("XOR_MERGE"), AND_SPLIT("AND_SPLIT"), AND_MERGE(
				"AND_MERGE"), OR_SPLIT("OR_SPLIT"), OR_MERGE("OR_MERGE"), OR_SPLIT_3(
				"OR_SPLIT_3"), OR_MERGE_3("OR_MERGE_3"), OR_SPLIT_4(
				"OR_SPLIT_4"), OR_MERGE_4("OR_MERGE_4");

		private String symbol;

		private Constraints(String symbol) {
			this.symbol = symbol;
		}

		public String mergeStatement(List<Arrow> l) {
			for (int i = 0; i < l.size(); i++) {
				System.out.println("Her MERGE: " + l.size() + " " + i + " "
						+ l.get(i).getTypeName());
				if ((l.get(i).getTypeName()).equalsIgnoreCase("loop")) {
					System.out.println("LOOP DETECTED MERGE");
					return orMerge(l);
				}
			}
			if (l.size() > 1) {
				switch (this) {
				case XOR_MERGE:
					System.out.println("XOR_MERGE");
					return xorMerge(l);
				case AND_MERGE:
					System.out.println("AND_MERGE");
					return andMerge(l);
				case OR_MERGE:
					System.out.println("## OR_MERGE");
					return orMerge(l);
				case OR_MERGE_3:
					System.out.println("## OR_MERGE_3");
					return orMerge3(l);
				case OR_MERGE_4:
					System.out.println("## OR_MERGE_4");
					return orMerge4(l);
				default:
					System.out.println("DEFAULT MERGE: LOOP_DETECTED");
					return orMerge(l);
				}
			} else if (l.size() == 1 && this == XOR_SPLIT) {
				// Case where we need to handle the c and !c predicate on a
				// single incoming arrow on a task node
				boolean c = false;

				for (Constraint cc : l.get(0).getConstraints()) {
					if (cc.getPredicate().getSymbol().equals("c")) {
						c = true;
					}
				}

				// return "disabled -> enabled { guard " + l.get(0).getName()
				// + " > 0 && " + (c ? "c == 1" : "c == 0") + "; },\n";
				return "disabled -> enabled { guard " + l.get(0).getName()
						+ " > 0; },\n";
			} else {
				return "disabled -> enabled { guard " + l.get(0).getName()
						+ " > 0; },\n";
			}

		}

		public String splitStatement(List<Arrow> l) {
			for (int i = 0; i < l.size(); i++) {
				System.out.println("Her SPLIT: " + l.size() + " " + i + " "
						+ l.get(i).getTypeName());
				if ((l.get(i).getTypeName()).equalsIgnoreCase("loop")) {
					System.out.println("LOOP DETECTED SPLIT");
					return xorSplit(l);
				}
			}
			if (l.size() > 1) {
				switch (this) {
				case XOR_SPLIT:
					System.out.println("XOR_SPLIT");
					return xorSplit(l);
				case AND_SPLIT:
					System.out.println("AND_SPLIT");
					return andSplit(l);
				case OR_SPLIT:
					System.out.println("OR_SPLIT");
					return orSplit(l);
				case OR_SPLIT_3:
					System.out.println("OR_SPLIT_3");
					return orSplit3(l);
				case OR_SPLIT_4:
					System.out.println("OR_SPLIT_4");
					return orSplit4(l);
				default:
					System.out.println("DEFAULT SPLIT: LOOP_DETECTED");
					return xorSplit(l);
				}
			} else {
				System.out.println("WE GET HERE SPLIT SIZE NOT 2: "
						+ l.get(0).getSource().getName());
				// String incomingArrow =
				// l.get(0).getSource().getIncomings().get(0).getName();
				return "running -> finished { effect " + l.get(0).getName()
						+ " = 1; };";
			}
		}

		public static Constraints getConstraint(String s) {
			for (Constraints c : Constraints.values()) {
				if (s.equals(c.symbol)) {
					return c;
				}
			}
			return null;
		}

	}

	public static String splitStatement(List<Arrow> l) {
		// All outgoing arrows should result in an effect statement
		try {
			Predicate p = l.get(0).getConstraints().get(0).getPredicate();
			Constraints c = Constraints.getConstraint(p.getSymbol());
			return c.splitStatement(l);
		} catch (IndexOutOfBoundsException e) {
			// Case where an arrow is not constrained. Should still result in a
			// effect expression
			System.out.println("WE GET HERE CATCH: "
					+ l.get(0).getSource().getName());
			if (l.get(0).getTypeArrow().getName().equalsIgnoreCase("loop")) {
				System.out.println("split one loop");
				return "running -> finished { effect " + l.get(0).getName()
						+ " = 1; };\n\t\t" + "running -> finished { effect "
						+ l.get(0).getName() + " = 0; };";
			}
			if (l.size() > 1
					&& (l.get(0).getTypeArrow().getName()
							.equalsIgnoreCase("loop") || l.get(1)
							.getTypeArrow().getName().equalsIgnoreCase("loop"))) {
				System.out.println("split one flow and one loop");
				return xorSplit(l);
				// "running -> finished { effect " + l.get(0).getName()
				// + " = 1; };\n\t\t" + "running -> finished { effect "
				// + l.get(0).getName() + " = 0; };";
			}

			return "running -> finished { effect " + l.get(0).getName()
					+ " = 1; };";
		}
	}

	public static String mergeStatement(List<Arrow> l) {
		// Assuming the incoming arrows are constrained by the same predicate
		try {
			Predicate p = l.get(0).getConstraints().get(0).getPredicate();
			Constraints c = Constraints.getConstraint(p.getSymbol());
			return c.mergeStatement(l);
		} catch (IndexOutOfBoundsException e) {
			// Case where an arrow is not constrained. Should still result in a
			// guard expression, unless it is a loop
			if (l.get(0).getTypeArrow().getName().equalsIgnoreCase("loop"))
				return "disabled -> enabled { },\n";
			return "disabled -> enabled { guard " + l.get(0).getName()
					+ " > 0; },\n";
		}
	}

	private static String andSplit(List<Arrow> l) {
		// AND split predicates shape consists of two arrows
		return "running -> finished { effect " + l.get(0).getName() + " = 1, "
				+ l.get(1).getName() + " = 1; };";
	}

	private static String orSplit(List<Arrow> l) {
		// OR split predicates shape consists of two arrows
		return "running -> finished { effect " + l.get(0).getName() + " = 1, "
				+ l.get(1).getName() + " = 1; }, "
				+ "running -> finished { effect " + l.get(0).getName()
				+ " = 0, " + l.get(1).getName() + " = 1; }, \n\t\t"
				+ "running -> finished { effect " + l.get(0).getName()
				+ " = 1, " + l.get(1).getName() + " = 0; }; ";
	}

	private static String orSplit3(List<Arrow> l) {
		// OR split predicates shape consists of three arrows
		return "running -> finished { effect " + l.get(0).getName() + " = 1, "
				+ l.get(1).getName() + " = 1, " + l.get(2).getName()
				+ " = 1; }, \n\t\t" + "running -> finished { effect "
				+ l.get(0).getName() + " = 1, " + l.get(1).getName() + " = 1, "
				+ l.get(2).getName() + " = 0; }, \n\t\t"
				+ "running -> finished { effect " + l.get(0).getName()
				+ " = 1, " + l.get(1).getName() + " = 0, " + l.get(2).getName()
				+ " = 0; }, \n\t\t" + "running -> finished { effect "
				+ l.get(0).getName() + " = 0, " + l.get(1).getName() + " = 1, "
				+ l.get(2).getName() + " = 0; }, \n\t\t"
				+ "running -> finished { effect " + l.get(0).getName()
				+ " = 0, " + l.get(1).getName() + " = 0, " + l.get(2).getName()
				+ " = 1; }, \n\t\t" + "running -> finished { effect "
				+ l.get(0).getName() + " = 1, " + l.get(1).getName() + " = 0, "
				+ l.get(2).getName() + " = 1; }, \n\t\t"
				+ "running -> finished { effect " + l.get(0).getName()
				+ " = 0, " + l.get(1).getName() + " = 1, " + l.get(2).getName()
				+ " = 1; }; ";
	}

	// All these methods with a number at the end should be parameterised, but I
	// dont have time for that now.
	private static String orSplit4(List<Arrow> l) {
		// AND split predicates shape consists of two arrows
		return "running -> finished { effect " + l.get(0).getName() + " = 1, "
				+ l.get(1).getName() + " = 1, " + l.get(2).getName() + " = 1, "
				+ l.get(3).getName() + " = 1; },\n\t\t"
				+ "running -> finished { effect " + l.get(0).getName()
				+ " = 1, " + l.get(1).getName() + " = 1, " + l.get(2).getName()
				+ " = 1, " + l.get(3).getName() + " = 0; },\n\t\t"
				+ "running -> finished { effect " + l.get(0).getName()
				+ " = 1, " + l.get(1).getName() + " = 1, " + l.get(2).getName()
				+ " = 0, " + l.get(3).getName() + " = 1; },\n\t\t"
				+ "running -> finished { effect " + l.get(0).getName()
				+ " = 1, " + l.get(1).getName() + " = 1, " + l.get(2).getName()
				+ " = 0, " + l.get(3).getName() + " = 0; },\n\t\t"
				+ "running -> finished { effect " + l.get(0).getName()
				+ " = 1, " + l.get(1).getName() + " = 0, " + l.get(2).getName()
				+ " = 1, " + l.get(3).getName() + " = 1; },\n\t\t"
				+ "running -> finished { effect " + l.get(0).getName()
				+ " = 1, " + l.get(1).getName() + " = 0, " + l.get(2).getName()
				+ " = 1, " + l.get(3).getName() + " = 0; },\n\t\t"
				+ "running -> finished { effect " + l.get(0).getName()
				+ " = 1, " + l.get(1).getName() + " = 0, " + l.get(2).getName()
				+ " = 0, " + l.get(3).getName() + " = 1; },\n\t\t"
				+ "running -> finished { effect " + l.get(0).getName()
				+ " = 1, " + l.get(1).getName() + " = 0, " + l.get(2).getName()
				+ " = 0, " + l.get(3).getName() + " = 0; },\n\t\t"
				+ "running -> finished { effect " + l.get(0).getName()
				+ " = 0, " + l.get(1).getName() + " = 1, " + l.get(2).getName()
				+ " = 1, " + l.get(3).getName() + " = 1; },\n\t\t"
				+ "running -> finished { effect " + l.get(0).getName()
				+ " = 0, " + l.get(1).getName() + " = 1, " + l.get(2).getName()
				+ " = 1, " + l.get(3).getName() + " = 0; },\n\t\t"
				+ "running -> finished { effect " + l.get(0).getName()
				+ " = 0, " + l.get(1).getName() + " = 1, " + l.get(2).getName()
				+ " = 0, " + l.get(3).getName() + " = 1; },\n\t\t"
				+ "running -> finished { effect " + l.get(0).getName()
				+ " = 0, " + l.get(1).getName() + " = 1, " + l.get(2).getName()
				+ " = 0, " + l.get(3).getName() + " = 0; },\n\t\t"
				+ "running -> finished { effect " + l.get(0).getName()
				+ " = 0, " + l.get(1).getName() + " = 0, " + l.get(2).getName()
				+ " = 1, " + l.get(3).getName() + " = 1; },\n\t\t"
				+ "running -> finished { effect " + l.get(0).getName()
				+ " = 0, " + l.get(1).getName() + " = 0, " + l.get(2).getName()
				+ " = 1, " + l.get(3).getName() + " = 0; },\n\t\t"
				+ "running -> finished { effect " + l.get(0).getName()
				+ " = 0, " + l.get(1).getName() + " = 0, " + l.get(2).getName()
				+ " = 0, " + l.get(3).getName() + " = 1; };";
	}

	private static String xorSplit(List<Arrow> l) {
		// XOR split predicates shape consists of two arrows
		StringBuffer ret = new StringBuffer();
		// ret.append("running -> finished { effect c = 1, " +
		// l.get(0).getName()
		// + " = 1, " + l.get(1).getName() + " = 0; },\n\t\t");
		// ret.append("running -> finished { effect c = 0, " +
		// l.get(1).getName()
		// + " = 1, " + l.get(0).getName() + " = 0; };");
		ret.append("running -> finished { effect " + l.get(0).getName()
				+ " = 1, " + l.get(1).getName() + " = 0; },\n\t\t");
		ret.append("running -> finished { effect " + l.get(1).getName()
				+ " = 1, " + l.get(0).getName() + " = 0; };");
		
		return ret.toString();
	}

	private static String xorMerge(List<Arrow> l) {
		// XOR merge predicates shape consists of two arrows
		StringBuffer ret = new StringBuffer();
		ret.append("disabled -> enabled { guard " + l.get(0).getName()
				+ " > 0 && " + l.get(1).getName() + " == 0; },\n\t\t");
		ret.append("disabled -> enabled { guard " + l.get(1).getName()
				+ " > 0 && " + l.get(0).getName() + " == 0; },\n");

		return ret.toString();
	}

	private static String orMerge(List<Arrow> l) {
		// XOR merge predicates shape consists of two arrows
		StringBuffer ret = new StringBuffer();
		ret.append("disabled -> enabled { guard " + l.get(0).getName()
				+ " > 0 || " + l.get(1).getName() + " > 0; },\n\t\t");
		
		return ret.toString();
	}

	private static String orMerge3(List<Arrow> l) {
		// XOR merge predicates shape consists of 3 arrows
		StringBuffer ret = new StringBuffer();
		ret.append("disabled -> enabled { guard " + l.get(0).getName()
				+ " > 0 || " + l.get(1).getName() + " > 0, "
				+ l.get(2).getName() + " > 0; },\n\t\t");
		
		return ret.toString();
	}

	private static String orMerge4(List<Arrow> l) {
		// XOR merge predicates shape consists of 4 arrows
		StringBuffer ret = new StringBuffer();
		ret.append("disabled -> enabled { guard " + l.get(0).getName()
				+ " > 0 || " + l.get(1).getName() + " > 0, "
				+ l.get(2).getName() + " > 0, " + l.get(3).getName()
				+ " > 0; },\n\t\t");
		
		return ret.toString();
	}

	private static String andMerge(List<Arrow> l) {
		// AND merge predicates shape consists of two arrows
		return "disabled -> enabled { guard " + l.get(0).getName() + " > 0 && "
				+ l.get(1).getName() + " > 0; },\n";
	}
}
